package com.ymx.darling.util;

import com.ymx.darling.bean.entity.BeanInfo;
import com.ymx.darling.bean.entity.BeanInfoManager;
import com.ymx.darling.bean.entity.FieldInfo;
import com.ymx.darling.exception.DarlingException;
import com.ymx.darling.wrapper.fragment.lambda.LambdaGetter;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class LambdaUtil {
    private static final Map<Class<?>, SerializedLambda> lambdaMap = new ConcurrentHashMap<>();
    private static final Map<String, Class<?>> classMap = new ConcurrentHashMap<>();

    private LambdaUtil() {
    }

    public static <T> String getFieldName(LambdaGetter<T> getter) {
        SerializedLambda lambda = getSerializedLambda(getter);
        String methodName = lambda.getImplMethodName();
        return StringUtil.methodToProperty(methodName);
    }

    public static <T> Class<?> getImplClass(LambdaGetter<T> getter) {
        SerializedLambda lambda = getSerializedLambda(getter);
        return getImplClass(lambda, getter.getClass().getClassLoader());
    }

    public static <T> String getColumnName(LambdaGetter<T> getter) {
        BeanInfo beanInfo = BeanInfoManager.getBeanInfo(getImplClass(getter));
        FieldInfo fieldInfo = beanInfo.getFieldMap().get(getFieldName(getter));
        String columnName = fieldInfo.getColumn();
        String tableName = beanInfo.getTableName();

        return tableName + "." + columnName;
    }

    private static SerializedLambda getSerializedLambda(Serializable getter) {
        return lambdaMap.computeIfAbsent(getter.getClass(), (aClass) -> {
            try {
                Method method = getter.getClass().getDeclaredMethod("writeReplace");
                method.setAccessible(Boolean.TRUE);
                return (SerializedLambda)method.invoke(getter);
            } catch (Exception var3) {
                throw new RuntimeException(var3);
            }
        });
    }

    private static Class<?> getImplClass(SerializedLambda lambda, ClassLoader classLoader) {
        String implClass = getImplClassName(lambda);
        return classMap.computeIfAbsent(implClass, (s) -> {
            try {
                return Class.forName(s.replace("/", "."), true, classLoader);
            } catch (ClassNotFoundException var3) {
                throw new DarlingException(var3, var3.getMessage());
            }
        });
    }

    private static String getImplClassName(SerializedLambda lambda) {
        String type = lambda.getInstantiatedMethodType();
        return type.substring(2, type.indexOf(";"));
    }
}

